/*
 *  Copyright 2010 mazhao.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package shopping.commons;

import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author mazhao
 */
public class Digester {

    // log object
    private static final Log log = LogFactory.getLog(Digester.class);
    
    // Zero for hex to string
    private static final String ZERO = "0";
    
    // algorithm, MD5 or SHA
    private static final String ALGORITHM = "MD5";

    // MessageDigest instance, initialization by static block.
    private static MessageDigest digest = null;

    /**
     * MessageDigest instance initialization block.
     */
    static {
        try {
            digest = MessageDigest.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException ex) {
            log.fatal(" !!! Can not get MessageDigest instance. !!! ", ex);
        }
    }

    /**
     * hash to string
     * @param hash input message digest
     * @return string of message digest
     */
    private static String hashByte2MD5(byte[] hash) {
        StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < hash.length; i++) {
            if ((0xff & hash[i]) < 0x10) {
                hexString.append(ZERO).append(Integer.toHexString((0xFF & hash[i])));
            } else {
                hexString.append(Integer.toHexString(0xFF & hash[i]));
            }
        }
        return hexString.toString();
    }

    /**
     * byte array to hash string
     * @param bytes
     * @return
     */
    public static String crypt(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            throw new IllegalArgumentException("bytes to encript cannot be null or zero length");
        }
        byte[] hash = digest.digest(bytes);

        return hashByte2MD5(hash);
    }

    /**
     * string to hash string.
     * @param str
     * @return
     */
    public static String crypt(String str) {
        if (str == null || str.length() == 0) {
            throw new IllegalArgumentException("bytes to encript cannot be null or zero length");
        }
        return crypt(str.getBytes());
    }

    /**
     * input stream to hash string
     * @param in
     * @return
     */
    public static String crypt(InputStream in) {
        try {
            if (in == null || in.available() == 0) {
                throw new IllegalArgumentException("InputStream can't be null or zero length.");
            }
            try {
                byte[] buff = new byte[4096];
                int len = 0;
                while ((len = in.read(buff, 0, buff.length)) >= 0) {
                    digest.update(buff, 0, len);
                }
            } catch (IOException e) {
                throw e;
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.info("Can not close input stream after reading the data.", e);
                    }
                }
            }
            byte[] hash = digest.digest();
            return hashByte2MD5(hash);
        } catch (IOException ex) {
            log.error("Throw IOException while digesting message.", ex);
            throw new RuntimeException("Throw IOException while digesting message.");
        }
    }
}
